package network

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

type HttpError struct {
	StatusCode int
	Err error
}

func (i *HttpError) Error() string {
	return fmt.Sprintf("StatusCode[%d],Error message[%s]",i.StatusCode,i.Err.Error())
}

func Error(code int,err string) *HttpError {
	return &HttpError{
		StatusCode: code,
		Err:        errors.New(err),
	}
}

type RequestCallback func(string,*HttpError)

func DoJsonAsync(url string,data string,timeout uint,callback RequestCallback) error {
	reader := bytes.NewReader([]byte(data))
	request,err := http.NewRequest("POST",url,reader)
	request.Header.Set("Content-Type","application/json;charset=UTF-8")
	if err != nil {
		return err
	}
	go doRequest(request,timeout,callback)
	return nil
}

func DoPostAsync(url string,form url.Values,timeout uint,callback RequestCallback) error {
	reader := bytes.NewReader([]byte(form.Encode()))
	request,err := http.NewRequest("POST",url,reader)
	request.Header.Set("Content-Type","application/x-www-form-urlencoded")
	if err != nil {
		return err
	}
	go doRequest(request,timeout,callback)
	return nil
}
func DoGetAsync(url string,timeout uint,callback RequestCallback) error {
	request,err := http.NewRequest("GET",url,nil)
	if err != nil {
		return err
	}
	go doRequest(request,timeout,callback)
	return nil
}

func doRequest(request *http.Request,timeout uint,callback RequestCallback) {
	ctx,cancel := context.WithTimeout(context.Background(),time.Duration(timeout) * time.Second)
	response,err := http.DefaultClient.Do(request.WithContext(ctx))
	if err != nil {
		if nil != cancel {
			cancel()
		}
		callback("",Error(http.StatusGatewayTimeout,err.Error()))
		return
	}

	data,err := ioutil.ReadAll(response.Body)
	if err != nil {
		callback("",Error(0,err.Error()))
		return
	}

	if response.StatusCode != http.StatusOK {
		callback("",Error(response.StatusCode,string(data)))
		return
	}

	callback(string(data),nil)

	defer func(){
		if nil != response {
			response.Body.Close()
		}
	}()
}
